package com.lagou.rpc.provider2.handler;

import com.alibaba.fastjson.JSON;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.provider2.anno.RpcService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务端业务处理类
 * 1，将标有@RpcService注解的bean进行缓存
 * 2. 接收客户端请求
 * 3. 根据传递过来的beanname从缓存中查找对应的bean
 * 4. 解析请求中的方法名称、参数类型、参数信息
 * 5. 反射调用bean的方法
 * 6. 给客户端进行响应
 */
@Component
@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {


    private static final Map SERVICE_INSTANCE_MAP = new ConcurrentHashMap();


    /**
     * 将标有@RpcService注解的bean进行缓存
     * 这个是ApplicationContextAware提供的方法
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if(serviceMap != null && serviceMap.size()>0){
            Set<Map.Entry<String, Object>> entries = serviceMap.entrySet();
            for (Map.Entry<String, Object> item : entries){
                Object serviceBean = item.getValue();
                //判断这个对象有没有实现接口
                Class<?>[] interfaces = serviceBean.getClass().getInterfaces();
                if(interfaces.length == 0){
                    throw new RuntimeException("服务必须实现接口");
                }
                //默认取第一个接口，作为这个bean的缓存名称
                String name = interfaces[0].getName();
                SERVICE_INSTANCE_MAP.put(name,serviceBean);

            }
        }
    }

    /**
     * 通道读取就绪事件
     * @param channelHandlerContext
     * @param s
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        //1. 接收客户端请求---将msg转换成rpc请求对象
        RpcRequest rpcRequest =  JSON.parseObject(msg,RpcRequest.class);

        //2。 创建rpc响应对象
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(rpcRequest.getRequestId());

        try {
            //具体调用业务处理
            Object result =  handler(rpcRequest);
            rpcResponse.setResult(result);

        }catch (Exception e){
            e.printStackTrace();
            rpcResponse.setError(e.getMessage());
        }

        //6. 给客户端进行响应
        channelHandlerContext.writeAndFlush(JSON.toJSONString(rpcResponse));



    }

    public Object handler(RpcRequest rpcRequest) throws InvocationTargetException {
        // 根据传递过来的beanname查找对应的bean对象
        Object serviceBean = SERVICE_INSTANCE_MAP.get(rpcRequest.getClassName());

        //判断是否找到，如果没找到抛异常
        if(serviceBean == null){
            throw new RuntimeException("根据beanname找不到服务，beaname："+rpcRequest.getClassName());
        }
        //解析方法名、参数类型、参数信息
        Class<?> aClass = serviceBean.getClass();
        String methodName = rpcRequest.getMethodName();
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();
        Object[] parameters = rpcRequest.getParameters();

        //通过反射调用方法--使用CGLib反射调用
        FastClass fastClass = FastClass.create(aClass);
        FastMethod method = fastClass.getMethod(methodName, parameterTypes);
        return  method.invoke(serviceBean,parameters);
    }
}
